/**
 *
 */
package com.onstar.cn.map;

import android.os.Message;
import android.util.Log;
import android.view.View;

import com.amap.api.maps2d.AMap;
import com.amap.api.maps2d.AMap.InfoWindowAdapter;
import com.amap.api.maps2d.AMap.OnMapClickListener;
import com.amap.api.maps2d.AMap.OnMarkerClickListener;
import com.amap.api.maps2d.model.BitmapDescriptorFactory;
import com.amap.api.maps2d.model.LatLng;
import com.amap.api.maps2d.model.LatLngBounds;
import com.amap.api.maps2d.model.Marker;
import com.amap.api.maps2d.model.MarkerOptions;
import com.amap.api.services.core.LatLonPoint;
import com.onstar.cn.map.common.Constants;
import com.onstar.cn.map.common.ExceptionType;
import com.onstar.cn.map.common.LocationType;
import com.onstar.cn.map.common.MapException;
import com.onstar.cn.map.common.PoiItemInfo;
import com.onstar.cn.map.common.RouteInfo;
import com.onstar.cn.map.core.OGeoPoint;
import com.onstar.cn.map.core.impl.OGeoPointImpl;
import com.onstar.cn.map.listener.ORouteHandlerListener;
import com.onstar.cn.map.sorter.CoordinatesSorter;
import com.onstar.cn.map.util.MapViewHelper;
import com.onstar.cn.map.util.MessageHandler;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This class is to perform location related functionality, such as show
 * location based on location type.
 *
 * @author Qiying Gong
 * @Created 2013-4-18
 */
public final class MapLocation implements OnMarkerClickListener, InfoWindowAdapter {

    private OMapActivity mMapActivity;
    private AMap mapView;

    private LatLonPoint mMyPointer;
    private LatLonPoint centerLocationPointer;

    private boolean isClickable = true;

    private MapViewHelper mapViewHelper;
    private MapRoute mapRoute;

    private PoiItemInfo searchedPoiItemInfo = null;
    private PoiItemInfo vehicleLocationPoiItemInfo = null;
    private PoiItemInfo phonePoiItemInfo = null;
    private PoiItemInfo footPrintPoiItemInfo = null;


    private MarkerOptions phoneOverlay = null;
    private MarkerOptions vehicleOverlay = null;
    private MarkerOptions poiOverlay = null;
    private MarkerOptions footPrintOverlay = null;

    private Marker phone;
    private Marker vehicle;
    private Marker poi;
    private Marker footPrint;


    private Map<LocationType, View> viewMap = null;

    private PoiItemInfo tempRouteStartPoiItemInfo = null;
    private PoiItemInfo tempRouteEndPoiItemInfo = null;
    private PoiItemInfo poiInfo = null;

    private MessageHandler mHandler;
    private LatLngBounds boundBox;

    public MapLocation(final OMapActivity mMapActivity, AMap mapView) {
        this.mMapActivity = mMapActivity;
        this.mapView = mapView;
        mapViewHelper = MapViewHelper.getInstance();
        mHandler = new MessageHandler(this.mapView);
        mapView.setOnMarkerClickListener(this);
        mapView.setInfoWindowAdapter(this);
        viewMap = new HashMap<LocationType, View>();
        mapView.setOnMapClickListener(new OnMapClickListener() {
            @Override
            public void onMapClick(LatLng arg0) {
                // 是否是显示的POI
                boolean isShowPoi = false;
                if (phone != null && phone.getPosition() != null) {
                    if (arg0 == phone.getPosition()) {
                        isShowPoi = true;
                    }
                } else if (vehicle != null && vehicle.getPosition() != null) {
                    if (arg0 == vehicle.getPosition()) {
                        isShowPoi = true;
                    }
                } else if (poi != null && poi.getPosition() != null) {
                    if (arg0 == poi.getPosition()) {
                        isShowPoi = true;
                    }
                }
                if (MapLocation.this.mMapActivity.getOnMapClickListener() != null && !isShowPoi) {
                    MapLocation.this.mMapActivity.getOnMapClickListener().onHideInfoWindow();
                }
            }
        });
    }

    /**
     * @param mapRoute the mapRoute to set
     */
    public void setMapRoute(MapRoute mapRoute) {
        this.mapRoute = mapRoute;
    }

    public void showLocation(final PoiItemInfo poiItemInfo, int resIcon, LocationType locationType) {
        if (poiItemInfo == null) {
            return;
        }
        this.poiInfo = poiItemInfo;
        setMyPointer(poiItemInfo.getLatitude(), poiItemInfo.getLongitude());
        if (locationType == null) {
            mapViewHelper.clearMapOverlays(mapView);
        }
        mapViewHelper.animateTo(mapView, mMyPointer);

        this.addOverlays();

    }

    public void showLocation(final OGeoPoint geoPoint, int resIcon) {
        // if (poiItemInfo == null) {
        // return;
        // }
        // this.poiInfo = poiItemInfo;
        setMyPointer(geoPoint.getLatitude(), geoPoint.getLongitude());
        mapViewHelper.clearMapOverlays(mapView);
        mapViewHelper.animateTo(mapView, mMyPointer);

        this.addOverlays();

    }

    public void registerMapRouteListener(ORouteHandlerListener listener) {
        mHandler.registerRouteListener(listener);
    }

    public void showPoiItemDetail() {
        mMapActivity.setPrintScreen(1);
        mMapActivity.showPoiItemDetailPage(this.poiInfo, null, false);

    }

    private void setMyPointer(double latit, double longit) {
        mMyPointer = mapViewHelper.getPointer(latit, longit);

    }

    private void addToList(List<Integer> lats, List<Integer> lons, double lat, double lon) {

        lats.add(OGeoPointImpl.convertToMicroDeg(lat));
        lons.add(OGeoPointImpl.convertToMicroDeg(lon));
    }

    private void configMapController() {
        List<Integer> latsList = new ArrayList<Integer>();
        List<Integer> lonsList = new ArrayList<Integer>();
        List<LatLng> listPts = new ArrayList<LatLng>();
        LatLngBounds.Builder boxBuilder = LatLngBounds.builder();
        if (null != phonePoiItemInfo) {
            addToList(latsList, lonsList, phonePoiItemInfo.getLatitude(), phonePoiItemInfo.getLongitude());
            listPts.add(new LatLng(phonePoiItemInfo.getLatitude(), phonePoiItemInfo.getLongitude()));
        }

        if (null != vehicleLocationPoiItemInfo) {
            addToList(latsList, lonsList, vehicleLocationPoiItemInfo.getLatitude(),
                    vehicleLocationPoiItemInfo.getLongitude());
            listPts.add(
                    new LatLng(vehicleLocationPoiItemInfo.getLatitude(), vehicleLocationPoiItemInfo.getLongitude()));
        }

        if (null != searchedPoiItemInfo) {
            addToList(latsList, lonsList, searchedPoiItemInfo.getLatitude(), searchedPoiItemInfo.getLongitude());
            listPts.add(new LatLng(searchedPoiItemInfo.getLatitude(), searchedPoiItemInfo.getLongitude()));
        }
        if (null != footPrintPoiItemInfo) {
            addToList(latsList, lonsList, footPrintPoiItemInfo.getLatitude(), footPrintPoiItemInfo.getLongitude());
            listPts.add(new LatLng(footPrintPoiItemInfo.getLatitude(), footPrintPoiItemInfo.getLongitude()));
        }

        if (latsList.size() == 0) {
            return;
        }

        Collections.sort(latsList, new CoordinatesSorter());
        Collections.sort(lonsList, new CoordinatesSorter());
        double minLat = OGeoPointImpl.convertToDeg(latsList.get(0));
        double minLon = OGeoPointImpl.convertToDeg(lonsList.get(0));
        double maxLat = OGeoPointImpl.convertToDeg(latsList.get(latsList.size() - 1));
        double maxLon = OGeoPointImpl.convertToDeg(lonsList.get(lonsList.size() - 1));

        double posLatitude = (minLat + maxLat) / 2;
        double posLongitude = (minLon + maxLon) / 2;
        centerLocationPointer = mapViewHelper.getPointer(posLatitude, posLongitude);
        if (listPts.size() > 1) {
            for (LatLng latLng : listPts) {
                boxBuilder.include(latLng);
            }
            this.boundBox = boxBuilder.build();
            mapViewHelper.zoomToSpan(mapView, boundBox);
            Log.w("*********bounding box:", boundBox.toString());
        }

    }

    public void showLocation(final LocationType locationType, final PoiItemInfo poiItemInfo) {
        showLocation(locationType, poiItemInfo, true);
    }

    public void showLocation(final LocationType locationType, final PoiItemInfo poiItemInfo, boolean isShowPopWindow) {
        try {
            if (poiItemInfo == null) {
                return;
            }
            setMyPointer(poiItemInfo.getLatitude(), poiItemInfo.getLongitude());
            // mapViewHelper.clearMapOverlays(mapView);

            Marker currentMarker = null;
            switch (locationType) {
                case MYSELF:
                    phonePoiItemInfo = poiItemInfo;
                    phonePoiItemInfo.setCurrentLocationTag(true);
                    phoneOverlay = createItemizedOverlay(locationType, poiItemInfo);

                    break;
                case VEHICLE:
                    vehicleLocationPoiItemInfo = poiItemInfo;
                    vehicleLocationPoiItemInfo.setCurrentLocationTag(false);
                    vehicleOverlay = createItemizedOverlay(locationType, poiItemInfo);

                    break;
                case POI:
                    searchedPoiItemInfo = poiItemInfo;
                    searchedPoiItemInfo.setCurrentLocationTag(false);
                    poiOverlay = createItemizedOverlay(locationType, poiItemInfo);
                    break;
                case FOOTPRINT:
                    footPrintPoiItemInfo = poiItemInfo;
                    footPrintPoiItemInfo.setCurrentLocationTag(false);
                    footPrintOverlay = createItemizedOverlay(locationType, poiItemInfo);
                    break;
                default:
                    break;
            }

            configMapController();
            addOverlays();
            switch (locationType) {
                case MYSELF:
                    currentMarker = this.phone;
                    break;
                case VEHICLE:
                    currentMarker = this.vehicle;
                    break;
                case POI:
                    currentMarker = this.poi;
                    break;
                case FOOTPRINT:
                    currentMarker = this.footPrint;
                    break;
                default:
                    break;
            }
            try{
                if (isShowPopWindow) {
                    if (currentMarker != null) {
                        currentMarker.showInfoWindow();
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }

            switch (locationType) {
                case MYSELF:
                    mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(phonePoiItemInfo.getLatitude(),
                            phonePoiItemInfo.getLongitude()));
                    break;
                case VEHICLE:
                    mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(vehicleLocationPoiItemInfo.getLatitude(),
                            vehicleLocationPoiItemInfo.getLongitude()));
                    break;
                case POI:
                    mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(searchedPoiItemInfo.getLatitude(),
                            searchedPoiItemInfo.getLongitude()));
                    break;
                case FOOTPRINT:
                    mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(footPrintPoiItemInfo.getLatitude(),
                            footPrintPoiItemInfo.getLongitude()));
                    break;
            }
            //只存在于一个poi点的时候
//			if (null != phonePoiItemInfo && null == searchedPoiItemInfo && null == vehicleLocationPoiItemInfo)
//				mapViewHelper.animateTo(mapView,
//						mapViewHelper.getPointer(phonePoiItemInfo.getLatitude(), phonePoiItemInfo.getLongitude()));
//			// else
//			// mapViewHelper.animateTo(mapView, centerLocationPointer);
//			else if (null != searchedPoiItemInfo && null != mapView ) {
//				mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(searchedPoiItemInfo.getLatitude(),
//						searchedPoiItemInfo.getLongitude()));
//			}
        } catch (java.lang.OutOfMemoryError e) {
            e.printStackTrace();
        }

    }

    public void showInfoWindow(final LocationType locationType) {
        configMapController();
        switch (locationType) {
            case MYSELF:
                mapViewHelper.animateTo(mapView,
                        mapViewHelper.getPointer(this.phone.getPosition().latitude, this.phone.getPosition().longitude));
                // this.phone.showInfoWindow();
                break;
            case VEHICLE:
                mapViewHelper.animateTo(mapView, mapViewHelper.getPointer(this.vehicle.getPosition().latitude,
                        this.vehicle.getPosition().longitude));
                this.vehicle.showInfoWindow();
                break;
            case POI:
                mapViewHelper.animateTo(mapView,
                        mapViewHelper.getPointer(this.poi.getPosition().latitude, this.poi.getPosition().longitude));
                this.poi.showInfoWindow();
                break;
            default:
                break;
        }
    }

    private void addOverlays() {
        if (this.poiOverlay != null) {
            destroyMarker(poi);
            this.poi = mapViewHelper.addOverlay(mapView, poiOverlay);
            this.poi.setObject(LocationType.POI);
        }
        if (this.vehicleOverlay != null) {
            destroyMarker(vehicle);
            this.vehicle = mapViewHelper.addOverlay(mapView, vehicleOverlay);
            this.vehicle.setObject(LocationType.VEHICLE);
        }
        if (this.phoneOverlay != null) {
            destroyMarker(phone);
            this.phone = mapViewHelper.addOverlay(mapView, phoneOverlay);
            this.phone.setObject(LocationType.MYSELF);
        }
        if (this.footPrintOverlay != null) {
//            destroyMarker(footPrint);
            this.footPrint = mapViewHelper.addOverlay(mapView, footPrintOverlay);
            this.footPrint.setObject(LocationType.FOOTPRINT);
        }
    }

    private void destroyMarker(Marker marker) {
        if (marker != null) {
            marker.remove();
        }
    }

    /**
     * @param locationType
     */
    private MarkerOptions createItemizedOverlay(final LocationType locationType, PoiItemInfo poiItemInfo) {

        MarkerOptions locationOverlay = null;
        Integer resIcon = null;
        switch (locationType) {
            case MYSELF:
                resIcon = mMapActivity.getmMyLocationArrow();
                break;
            case VEHICLE:
                resIcon = mMapActivity.getmVehicleLocationArrow();
                break;
            case POI:
                resIcon = mMapActivity.getmPoiLocationArrow();
                break;
            case FOOTPRINT:
                resIcon = mMapActivity.getmFootPrintArror();
                break;
            default:
                resIcon = mMapActivity.getmMyLocationArrow();
        }
        locationOverlay = new MarkerOptions()
                .position(mapViewHelper.getLatLng(poiItemInfo.getLatitude(), poiItemInfo.getLongitude()))
                .icon(BitmapDescriptorFactory.fromResource(resIcon)).title(poiItemInfo.getTitle())
                .snippet(poiItemInfo.getAddress()).draggable(false).anchor(0.465F, 0.465F);

        return locationOverlay;
    }

    /**
     *
     */
    public void clickHandler(Marker marker) {

        PoiItemInfo itemInfo = null;
        LocationType locationType = (LocationType) marker.getObject();
        if (locationType == null) {
            marker.hideInfoWindow();
            return;
        }
        mapViewHelper.clearRouteOverlay(mapView, mapRoute.getRouteOverlay());
        mapViewHelper.removeView(mapView, mapRoute.getStartLayout());
        mapViewHelper.removeView(mapView, mapRoute.getEndLayout());

        switch (locationType) {
            case MYSELF:
                itemInfo = phonePoiItemInfo;
                break;
            case VEHICLE:
                itemInfo = this.vehicleLocationPoiItemInfo;
                break;
            case POI:
                itemInfo = this.searchedPoiItemInfo;
                break;
            default:
                break;
        }

        if (null == tempRouteStartPoiItemInfo) {
            tempRouteStartPoiItemInfo = itemInfo;
            handleMessage(Constants.ROUTE_START_POINT, locationType);
            isClickable = false;

            new java.util.Timer().schedule(new java.util.TimerTask() {
                public void run() {
                    isClickable = true;
                    this.cancel();
                }
            }, 500);

        } else if (null == tempRouteEndPoiItemInfo && !isSamePoint(tempRouteStartPoiItemInfo, itemInfo)) {
            tempRouteEndPoiItemInfo = itemInfo;
            handleMessage(Constants.ROUTE_END_POINT, locationType);
        } else {

            if (null != mapRoute.getRouteOverlay()) {
                tempRouteStartPoiItemInfo = itemInfo;
                handleMessage(Constants.ROUTE_START_POINT, locationType);
                tempRouteEndPoiItemInfo = null;
            }

        }

        if (null != tempRouteStartPoiItemInfo && null != tempRouteEndPoiItemInfo
                && !tempRouteStartPoiItemInfo.equals(tempRouteEndPoiItemInfo)) {
            /*
			 * Here to call calculate temp route.
			 */
            RouteInfo info = new RouteInfo();
            info.setEndItem(tempRouteEndPoiItemInfo);
            info.setStartItem(tempRouteStartPoiItemInfo);
            info.setRoute(this.mapRoute);
            info.setId(System.currentTimeMillis());

            handleMessage(Constants.CALCULATE_TEMP_ROUTE, info);
        }
    }

    private void handleMessage(int value, Object obj) {
        mHandler.sendMessage(Message.obtain(mHandler, value, obj));
    }

    /**
     * Check the two points to see if they are too close to calculate route.
     *
     * @return true if the distance less than 5 meter, otherwise false
     */
    private boolean isSamePoint(PoiItemInfo itemInfo1, PoiItemInfo itemInfo2) {
        boolean same = mapViewHelper.isSamePoint(itemInfo1, itemInfo2);
        if (same) {
            String errMsg = "The distance less than minimum distance for calculating route";
            MapException error = new MapException(0, errMsg, new Exception(errMsg), ExceptionType.LESS_MIN_ROUTE_DIST);
            handleMessage(Constants.ROUTE_SEARCH_ERROR, error);
        }
        return same;
    }

    public LatLngBounds getBoundBox() {
        return this.boundBox;
    }

    protected LatLonPoint getCenterLocationPointer() {
        return centerLocationPointer;
    }

    public void removeListener() {
        mHandler.removeRouteListener();
    }

    public void clearLocation() {
        // mapView.getOverlays().remove(this.poiOverlay);
        // mapView.getOverlays().remove(this.phoneOverlay);
        // mapView.getOverlays().remove(this.vehicleOverlay);
        this.phoneOverlay = null;
        this.poiOverlay = null;
        this.vehicleOverlay = null;
        this.phonePoiItemInfo = null;
        this.vehicleLocationPoiItemInfo = null;
        this.searchedPoiItemInfo = null;
    }

    public void clearTempRoutePoints() {
        this.tempRouteEndPoiItemInfo = null;
        this.tempRouteStartPoiItemInfo = null;
    }

    public void setInfoContentView(View view, LocationType type) {
        this.viewMap.put(type, view);
    }

    /**
     *
     */
    @Override
    public boolean onMarkerClick(Marker marker) {

        if (this.isClickable) {
            if (marker.getPosition() != null)
                this.clickHandler(marker);
        }
        marker.hideInfoWindow();
        if (mMapActivity.getOnMapClickListener() != null) {
            LocationType type = (LocationType) marker.getObject();
            if (type != null) {
                mMapActivity.getOnMapClickListener().onMarkerClick(type, marker);
            }
        }
        // arg0.showInfoWindow();
        return true;
    }

    @Override
    public View getInfoContents(Marker marker) {
        // LocationType type = (LocationType) marker.getObject();
        // if (type == null) {
        // return null;
        // }
        // return this.viewMap.get(type);
        return null;
    }

    @Override
    public View getInfoWindow(Marker marker) {
        LocationType type = (LocationType) marker.getObject();
        if (type == null) {
            return null;
        }
        return this.viewMap.get(type);
    }

}
